package lesson

import (
	"fmt"
	"reflect"
)

func Lesson09() {
	fmt.Println("package lesson file lesson/lesson09.go function Lesson09")
	func01()
	func02(1, "hello world")
	func03(1, 2, 3)
	func04()
	func05()
	func06()
	func06(1, 2, 3, 4, 5, 6)
	func07("hello world", 1, 2, 3)
	func07("hello world", 1, 2, 3, 4, 5, 6, 7)

	fmt.Println()
	fmt.Println("test func08")
	value01, value02 := 1, 2
	fmt.Println("[value01]:", value01, " [value02]:", value02)
	func08(value01, value02)
	fmt.Println("[value01]:", value01, " [value02]:", value02)

	fmt.Println()
	fmt.Println("test func09")
	slice := []int{1, 2, 3, 4}
	fmt.Println("[slice]:", slice)
	func09(slice)
	fmt.Println("[slice]:", slice)

	fmt.Println()
	fmt.Println("test func10")
	fmt.Println("[value01]:", value01, "[value01.address]:", &value01)
	func10(&value01)
	fmt.Println("[value01]:", value01, "[value01.address]:", &value01)

	// 函数名称作为变量
	fmt.Println()
	fmt.Println("test func11")
	function01 := func01
	function01()

	// 匿名函数
	fmt.Println()
	fmt.Println("test func12")
	function02 := func() {
		fmt.Println("package lesson file lesson/lesson09.go function Anonymous Function ")
	}
	function02()

	// 闭包
	fmt.Println()
	function03 := closure(10)
	fmt.Println(function03(1))
	fmt.Println(function03(2))

	//defer
	defer01()
	defer02()
	defer03()
	defer04()
	homework()

}

// 函数基本写法
func func01() {
	fmt.Println("package lesson file lesson/lesson09.go function func01")
}

// 一般的函数参数
func func02(a int, b string) {
	fmt.Println("package lesson file lesson/lesson09.go function func02")
	fmt.Println("func get value [a]:", a, " [b]:", b)
}

// 函数参数省略写法
func func03(a, b, c int) {
	fmt.Println("package lesson file lesson/lesson09.go function func03")
	fmt.Println("func get value [a]:", a, " [b]:", b, " [c]:", c)
}

// 多返回值 - 建议使用这种
func func04() (int, int, int) {
	fmt.Println("package lesson file lesson/lesson09.go function func04")
	a, b, c := 1, 2, 3
	return a, b, c
}

// 多返回值省略写法
func func05() (a, b, c int) {
	fmt.Println("package lesson file lesson/lesson09.go function func05")
	a, b, c = 1, 2, 3
	return
}

// 不定长度变量参数
func func06(a ...int) {
	fmt.Println("package lesson file lesson/lesson09.go function func06")
	fmt.Println("[a]:", a, " [a.type]:", reflect.TypeOf(a))
}

// 固定变量与不定长度变量参数混合使用
func func07(b string, a ...int) {
	fmt.Println("package lesson file lesson/lesson09.go function func07")
	fmt.Println("[b]:", b, " [a]:", a, " [a.type]:", reflect.TypeOf(a))
}

// 不定长度变量的函数，不会对外发生变更
func func08(slice ...int) {
	fmt.Println("package lesson file lesson/lesson09.go function func08")
	fmt.Println("[slice]:", slice)
	slice[0] = 3
	slice[1] = 4
	fmt.Println("[slice]:", slice)
}

// 定常的slice作为变量传入函数，会造成外部的slice发生变更
// 这种方式进行的拷贝是slice当中地址的拷贝，也就是说操作其中的数据直接产生影响
func func09(slice []int) {
	fmt.Println("package lesson file lesson/lesson09.go function func09")
	fmt.Println("[func09.slice]:", slice)
	slice[0] = 3
	slice[1] = 4
	fmt.Println("[func09.slice]:", slice)
}

// 引用类型的传递
func func10(a *int) {
	fmt.Println("package lesson file lesson/lesson09.go function func10")
	*a = 2
	fmt.Println("[a.address]:", a, "[a.value]:", *a)
}

// 闭包
func closure(x int) func(int) int {
	fmt.Println("[&x]:", &x)
	return func(y int) int {
		fmt.Println("[&x]:", &x)
		return x + y
	}
}

func defer01() {
	fmt.Println("package lesson file lesson/lesson09.go function defer01")
	fmt.Println("a")
	defer fmt.Println("b")
	defer fmt.Println("c")
}

func defer02() {
	fmt.Println("package lesson file lesson/lesson09.go function defer02")
	for index := 0; index < 3; index++ {
		defer fmt.Println("[index]:", index)
	}
}

// 使用了闭包
func defer03() {
	fmt.Println("package lesson file lesson/lesson09.go function defer03")

	for index := 0; index < 3; index++ {
		defer func() {
			fmt.Println("[index]:", index)
		}() // 调用这个函数
	}
}

func defer04() {
	fmt.Println()
	fmt.Println("package lesson file lesson/lesson09.go function defer04")
	functionPrintA()
	functionPanicB()
	functionPrintC()
}

func functionPrintA() {
	fmt.Println("package lesson file lesson/lesson09.go function functionPrintA")
}

func functionPanicB() {
	// 在panic之前设置defer函数，才能够让defer被访问到
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("Recover in B")
		}
	}()
	panic("package lesson file lesson/lesson09.go panic functionPanicB")
}

func functionPrintC() {
	fmt.Println("package lesson file lesson/lesson09.go function functionPrintC")
}

func homework() {
	fmt.Println()
	fmt.Println("package lesson file lesson/lesson09.go function homework")
	var fs = [4]func(){}

	for index := 0; index < 4; index++ {
		defer fmt.Println("defer [index]:", index)
		defer func() {
			fmt.Println("defer_closure [index]:", index)
		}()

		fs[index] = func() {
			fmt.Println("closure [index]:", index)
		}
	}

	for _, f := range fs {
		f()
	}
}
